// Copyright 2013 Beego Samples authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package controllers

import (
	"container/list"
	"time"

	"github.com/astaxie/beego"
	"github.com/gorilla/websocket"

	"gitee.com/lzqysoft/ims/models"
)

// Subscription 连接者消息
type Subscription struct {
	Archive []models.WsEvent      // All the events from the archive.
	New     <-chan models.WsEvent // New events coming in.
}

//newEvent 创建新websocket用户
func newEvent(ep, username string, userid string, targetid, roomid, msg string, userlist []interface{}) models.WsEvent {
	return models.WsEvent{ep, username, userid, targetid, roomid, int(time.Now().Unix()), msg, userlist}
}

//Join 建立连接
func Join(userid string, username, roomid string, ws *websocket.Conn) {
	subscribe <- Subscriber{Id: userid, Name: username, RoomId: roomid, Conn: ws}
}

//Leave 断开连接
func Leave(userid string, username, roomid string, ws *websocket.Conn) {
	unsubscribe <- Subscriber{Id: userid, Name: username, RoomId: roomid, Conn: ws}
}

//Subscriber 连接者
type Subscriber struct {
	Id     string          `json:"userid,omitempty"`
	Name   string          `json:"name,omitempty"`
	Avatar string          `json:"avatar,omitempty"` //头像
	RoomId string          `json:"roomId,omitempty"`
	Conn   *websocket.Conn // Only for WebSocket users; otherwise nil.
}

var (
	//新建立用户频道chan.
	subscribe = make(chan Subscriber, 10)
	//退出用户频道chan.
	unsubscribe = make(chan Subscriber, 10)
	//发送事件发布。chan
	publish = make(chan Message, 10)
	//长轮询候补名单。队列chan
	waitingList = list.New()
	//用户队列链表
	subscribers = list.New()
)

//chatroom 此函数处理所有传入的chan消息。
func chatroom() {
	for {
		select {
		//监听建立连接chan有数据
		case sub := <-subscribe:
			if !isUserExist(subscribers, sub.Id) {
				subscribers.PushBack(sub) //将用户添加到列表的末尾。.
				// //发布JOIN事件。
				userlist := getUserlist(subscribers, sub.RoomId)
				evt := newEvent(models.EVENT_JOIN, sub.Name, sub.Id, sub.Avatar, sub.RoomId, "", userlist)
				broadcastWebSocket(evt)
				welcomevt := newEvent(models.WELCOM_MESSAGE, sub.Name, sub.Id, sub.Avatar, sub.RoomId, "欢迎"+sub.Name+"加入聊天，请文明发言！", userlist)
				broadcastWebSocket(welcomevt)
				models.NewArchive(evt)
				beego.Info("New user:", sub.Id, ";WebSocket:", sub.Conn != nil)
			} else {
				for unsub := subscribers.Front(); unsub != nil; unsub = unsub.Next() {
					if unsub.Value.(Subscriber).Id == sub.Id {
						subscribers.Remove(unsub)
						userlist := getUserlist(subscribers, sub.RoomId)
						evt := newEvent(models.EVENT_LEAVE, sub.Name, sub.Id, sub.Avatar, sub.RoomId, "", userlist)
						models.NewArchive(evt)
					}
				}
				subscribers.PushBack(sub)
				userlist := getUserlist(subscribers, sub.RoomId)
				beego.Info("userlist userlist:", userlist)
				evt := newEvent(models.EVENT_JOIN, sub.Name, sub.Id, sub.Avatar, sub.RoomId, "", userlist)
				broadcastWebSocket(evt)
				models.NewArchive(evt)
				beego.Info("Old user:", sub.Id, ";WebSocket:", sub.Conn != nil)
			}
			//监听发布信息chan有数据
		case msg := <-publish:
			// 通知等待队列。
			for ch := waitingList.Back(); ch != nil; ch = ch.Prev() {
				ch.Value.(chan bool) <- true
				waitingList.Remove(ch)
			}
			event := msg.Evt
			switch event.Type {
			case "EVENT_JOIN":

				Join(event.UserId, event.UserName, event.RoomId, msg.Ws)

			case "EVENT_MESSAGE":
				// Send it out to every client that is currently connected
				broadcastWebSocket(event)
				models.NewArchive(event)
			case "EVENT_LEAVE":
				Leave(event.UserId, event.UserName, event.RoomId, msg.Ws)

			default:
			}

			//监听退出用户频道chan有数据
		case unsub := <-unsubscribe:
			for sub := subscribers.Front(); sub != nil; sub = sub.Next() {
				if sub.Value.(Subscriber).Id == unsub.Id {
					beego.Debug("unsubunsubunsub:", unsub)
					subscribers.Remove(sub)
					userlist := getUserlist(subscribers, unsub.RoomId)
					evt := newEvent(models.EVENT_LEAVE, unsub.Name, unsub.Id, unsub.Avatar, unsub.RoomId, "", userlist)
					//					publish <- Message{ws, }
					broadcastWebSocket(evt) // Publish a LEAVE event.
					models.NewArchive(evt)
					break
				}
			}
		}
	}
}

func init() {
	go chatroom()
}

func isUserExist(subscribers *list.List, userid string) bool {
	for sub := subscribers.Front(); sub != nil; sub = sub.Next() {
		if sub.Value.(Subscriber).Id == userid {
			return true
		}
	}
	return false
}

//getUserlist 获取该房间用户列表
func getUserlist(subscribers *list.List, roomid string) []interface{} {
	userlist := make([]interface{}, 0)
	for sub := subscribers.Front(); sub != nil; sub = sub.Next() {
		if sub.Value.(Subscriber).RoomId == roomid {
			userlist = append(userlist, sub.Value.(Subscriber))
		}
	}
	return userlist
}
